/*
Creation date:	from 29/04/2003 to 01/04/2003

Autor:			Mountaniol Vsevolod <sebastian@vboxcomm.com>

File:			updater.c

Description:	Create updates for XLT / XR1 systems. 
				Also apply update on target machines.
*/

#include <sys/types.h> 	/* open, stat */
#include <sys/stat.h> 	/* open, stat */
#include <fcntl.h> 		/* open, stat */
#include <unistd.h>		/* close, stat, mmap, readdir */
#include <sys/mman.h>	/* mmap */
#include <dirent.h>		/* readdir */

#include <stdio.h>		/* ead */
#include <errno.h>		/* errors */
#include <string.h>		/* strcpy, str* */
#include <stdlib.h>		/* malloc */
#include <zlib.h>
#include "updater.h"

#ifdef DEBUG
#	define DD printf
#else
#	define DD null_function
#endif

#define tfree(x) {if ( x != NULL ) free(x); }

/*	functions prototypes */
static int 				decompress_file (char * file_name_in);
static int 				__decompress_file (char * file_name_in);
static int 				__compress_file  (char *);
static int 				compress_file  (struct flist * fl);
static struct flist * 	add_file(struct flist * fl, char * file_name);
static struct flist * 	read_spec(char * file_name);
#ifdef DEBUG
static void 			print_fl(struct flist *fl);
#endif
static int 				validate_files(struct flist *fl);
static int 				concat_files (char * file_name_out, struct flist * fl);
static int 				list_files(char * file_name);
static int				write_description_and_name (char * file_name, char *, char *);
static char * 			extract_dir_name ( const char *file_name );
static int 				create_path (char * file_name);
int 					file (struct flist *fl, char * file_name);

/*
	FUNCTION:	print_usage.
	IN:			nothing.
	OUT:		nothing.
	Print usage of the utility.
*/

void null_function(char * x,...) {
}

void print_usage() {
//  printf("Usage: updater 'update_file_name'\n");
  printf("Usage: updater 'action (c | d | l | m | n)' 'update_file' ['spec_file' | 'name' | 'description']\n");
  printf("action: c - compress files. Names of files will read from 'spec_file'. Result file is 'update_file'\n");
  printf("action: d - decompress file 'update_file' .\n");
  printf("action: l - list of 'update_file' .\n");
  printf("action: n - write 'name' of update into 'update_file' .\n");
  printf("action: m - write 'description' of update into 'update_file' .\n");
}



/*
	FUNCTION:	main
	IN:			int argn, char ** args
	OUT:		status.
	Entry point of utility. Get command line. Return status: 0 - success, else
	- error..
*/


int main(int argn, char ** args) {

  struct flist * fl;
  int rv;

  if (argn < 3) {
	print_usage();
	return - ECANCELED;
  }

  switch (args[1][0]) {
	case 'c':
	  printf("Creating update\n");
	  fl = read_spec (args[3]);
	  if (fl == NULL) {
		printf ("Error on spec reading. Please check spec file '%s'\n",args[3]);
		return - ECANCELED;
	  }
	  rv = validate_files(fl);
	  if (rv) return rv;
	  rv = compress_file(fl);
	  if (rv != 0) {
		printf ("Error on files compressing stage\n");
		return - ECANCELED;
	  }
	  
	  rv = concat_files(args[2], fl);
	  if (rv != 0) {
		printf ("Error on concatination\n");
		return - ECANCELED;
	  }
	  return 0;
	  break;
	case 'd':
	  printf("Updating\n");
	  return decompress_file (args[2]);
	case 'l':
	  return list_files(args[2]);
	case 'n':
	  rv = write_description_and_name (args[2], NULL, args[3]);
	  if (rv != 0) {
		printf("Error on writing update name\n");
	  }
	  break;
	case 'm':
	  rv = write_description_and_name (args[2],args[3], NULL);
	  if (rv != 0) {
		printf("Error on writing update description\n");
	  }
	  break;
	default:
	  printf("Unknown parameter: %s\n", args[1]);
	  print_usage();
	  return - ENOSYS;
	  break;
  }

  return 0;
}


/*
	FUNCTION:	construct_name
	IN:			char * file_name_in, char flag
	OUT:		char * 
	Get name of an input file. Construct name for output file.
	Compressed   output file has suffix '.cmp'.
	Decompressed output file has suffix '.dcp'.
 
 */

char * construct_name (char * file_name_in, char flag) {
  
  char * file_name_out;
  if (file_name_in == NULL) {
	return NULL;
  }

    
  file_name_out = (char *) malloc(strlen(file_name_in) + 5);

  if (file_name_out == NULL) {
	printf("Low memory\n");
	return NULL;
  }
  
  /*    construct out file name from source + '.gz' suffix */
  strcpy(file_name_out, file_name_in);

  if (flag == F_COMPRESS) {
	strcat(file_name_out, ".cmp");
  }

  if (flag == F_DECOMPRESS) {
	strcat(file_name_out, ".dcp");
  }

  DD("constructed: %s\n", file_name_out);
  return file_name_out;
		  
}


/*
	FUNCTION: compress_file
	IN: struct flist *
	OUT: int
	Get linked list of file names.
	Call for every file compress function (__compress_file)
*/

int compress_file (struct flist * fl) {
  struct flist * fl_copy = fl;
  int rv;
  if (fl == NULL) {
	return - EINVAL;
  }

  while (fl_copy != NULL) {
	DD("Compress file '%s'\n", fl_copy->file_name);
	rv = __compress_file(fl_copy->file_name);

	if (rv != 0) {
	  return rv;
	}
	
	fl_copy = fl_copy->next;
  }

  return 0;
  
}


/* 
	FUNCTION: 	__compress_file
	IN: 		file name.
	OUT: 		status, where 0 - success, else - error.
	Get file name of 'source_file' . 
	Open the file, compress it into 'source_file.cmp'
*/

int __compress_file(char * file_name_in) {
  char *file_name_out;
  Bytef * buff_little  = alloca(BUFF_LITTLE);
  Bytef * buff_big = alloca(BUFF_BIG);
  FD_T * fd_in;
  FD_T * fd_out;
  INT rv;
  INT rv_out;
  unsigned long not_compressed_data_size;
  unsigned long compressed_data_size;
  unsigned long size_passed = 0;
  struct file_header * fh;

  /*	construct output file name */
  file_name_out = construct_name(file_name_in, F_COMPRESS);

  if (file_name_out == NULL) {
	return - ECANCELED;
  }

  /*	allocate memory for file header */
  fh = (struct file_header *) malloc(sizeof(struct file_header));

  if (fh == NULL ) {
	perror("Low mem: ");
	return - ENOMEM;
  }

  /*	clean file header */
  w_memset (fh, 0, sizeof(struct file_header));

  /*	read input file stat */	
  rv = w_stat(file_name_in, &fh->stat);
  if (rv) {
	perror("Can not stat file: ");
	return - ECANCELED;
 }

  /*	fill file header */
  fh->major = 0;
  fh->minor = 1;
  strcpy (fh->path, file_name_in);
  strcpy (fh->description, "This is just test");
  memcpy (fh->singature, SINGATURE, SIZE_OF_SINGATURE);

  /*	open input file */
  fd_in = w_open (file_name_in, O_RDONLY);
  if ( w_filestatus(fd_in) < 0) {
	perror("Cant open source: ");
	return - EIO;
  }

  /*	open output file */
  fd_out = w_open (file_name_out, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRWXG |  S_IRGRP);
  if (w_filestatus(fd_out) < 0) {
	perror("Cant open dest: ");
	w_close(fd_in);
	return - EIO;
  }

  /*	write file header */
  rv = w_write(fd_out, fh, sizeof(struct file_header));
  if (rv != sizeof(struct file_header)) {
	perror("Can't write file_header to header:");
	w_close(fd_in);
	w_close(fd_out);
	return - EIO;
  }

  /*	read input file, compress / decompress , and write into output file */
  do {
	not_compressed_data_size = w_read (fd_in, buff_little, BUFF_LITTLE);
//	DD ("read: %i\n", rv);

	/*	did it read something? */
	if (not_compressed_data_size > 0) {

	  compressed_data_size = BUFF_BIG;

	  /*	yes it did. Compress / decompress buffer */
	  rv_out = compress2 (buff_big, &compressed_data_size, buff_little, not_compressed_data_size, 9);
	
	  /*	check was there error or not */
	  switch (rv_out) {
		case Z_MEM_ERROR:
			printf("Compression memory error\n");
		  	w_close(fd_in);
			w_close(fd_out);
			return - ENOMEM;

		case Z_BUF_ERROR:
			printf("Output buffer too small\n");
		  	w_close(fd_in);
			w_close(fd_out);
			return - ENOMEM;

		default:
//		  printf("out: %u\n", (unsigned int) compressed_data_size);
		  size_passed += not_compressed_data_size; 
		  printf("\r[%.10u / %.10u] - %s", (unsigned int) size_passed, (unsigned int) fh->stat.st_size,  fh->path);
		  break;
	  }

	  /*	no errors due compress. Write the buffer into output file */ 

	  /*	1. write the len of compressed buff - I need it for decompress  */
	  rv_out = w_write (fd_out, &compressed_data_size, sizeof(compressed_data_size));

	  /* 	2. write the len of original not compressed buffer */
	  rv_out = w_write (fd_out, &not_compressed_data_size, sizeof(not_compressed_data_size));

	  /*	3. write down compressed date itself */
	  rv_out = w_write (fd_out, buff_big, compressed_data_size);

	  /*	wrote ok? */
	  if (rv_out != compressed_data_size ) {
		  printf("Error writing compressed date!\n");
		  w_close(fd_in);
		  w_close(fd_out);
		  return - EIO;
	  }
	}
  } while (not_compressed_data_size > 0);

  printf("\n");
  

  /*	close files */
  w_close (fd_out);
  w_close (fd_in);

  /*	free buffer */
  tfree(file_name_out);

  /*	exit */
  return 0;
}



int decompress_file (char * file_name_in) {
  FD_T * fd_in;
  FD_T * fd_out;
  INT rv;
  struct file_header * fh;
  size_t offset;
  char * buf, *file_name_out;
  

  if (file_name_in == NULL) {
	return - EINVAL;
  }

  /*	open the file */
  fd_in = w_open (file_name_in, O_RDONLY);

  if ( w_filestatus(fd_in) < 0) {
	return - EIO;
  }

  /*	allocate memory for the file header */
  fh = alloca ( sizeof ( struct file_header ) );
  if ( fh == NULL ) {
	return - ENOMEM;
  }

  /*	allocate memory for buffer */
  buf = alloca(BUFF_BIG);
  if (buf == NULL) {
	perror("Low memory");
	return - ENOMEM;
  }

  /*	read the header */
  rv = w_read(fd_in, fh, sizeof(struct file_header));
  if (rv != sizeof(struct file_header)) {
 	perror("Can't read file header");
	w_close(fd_in);
	w_close(fd_out);
 	return - EIO;
  }

  if (CHECK_FLAG(fh->flags, UPD_HAS_PREINSTALL)) {
	printf ("Run preinstall\n");
	system("./update.run");
	printf ("End preinstall\n");
  }

  do {

	/*read the len of file */
	rv = w_read (fd_in, &offset, sizeof(offset));
	if (rv != sizeof(offset)) {
	  /*	probably end of file */
	  continue;
	}

	/*	get a name for a temporary file */
	file_name_out = tempnam("/tmp/", NULL);
	if (file_name_out == NULL) {
	  perror("Can't generate a name for a temporary file");
	  w_close(fd_in);
	  w_close(fd_out);
	  return - errno;
	}

	DD("Temporary name: %s\n", file_name_out);

	/* 	open the temporary file*/
	fd_out = w_open (file_name_out, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRWXG | S_IRGRP);
	if (fd_out < 0) {
	  perror("Can't open temporary file");
	  w_close(fd_in);
	  w_close(fd_out);
	  return - EIO;
	}

	{
	  int _i, _len, _rv;

  	  for (_i = 0; _i < offset ;) {

		/*	count len to read / write */
		_len = (offset - _i> BUFF_BIG)? BUFF_BIG : offset - _i;


		/*	read buf */	
		_rv = w_read(fd_in, buf, _len);
		if (_rv != _len) {
			perror("Can't read from input file - error") ;
			DD("Asked: %i, read: %i",_len,_rv);
		  	w_close(fd_in);
			w_close(fd_out);
			return - EIO;
		}

		/*	write buf */
		_rv = w_write (fd_out, buf, _len);
		if (_rv != _len) {
		  perror("Can't write into output file - error");
		  	w_close(fd_in);
			w_close(fd_out);
		  return - EIO;
		}

		_i += _len;
		
		
  	  } /* end for */
	}

	w_close (fd_out);
	__decompress_file(file_name_out);
	

  } while (rv > 0);

  w_close (fd_in);
  if (CHECK_FLAG(fh->flags, UPD_HAS_POSTINSTALL)) {
	printf ("Run preinstall\n");
	system("./update.run");
	printf ("End preinstall\n");
  }

  tfree(fh);
  
  return 0;
}


/* 
	FUNCTION: 	__decompress_file
	IN: 		file name.
	OUT: 		status, where 0 - success, else - error.
	Decompress file. Get file 'compressed_file'. 
	Decompress it into 'compressed_file.cmp'.
	After this rename 'compressed_file.cmp' into his original name wich saved
	in header when file was compressed.
*/

int __decompress_file(char * file_name_in) {

  char *file_name_out;
  Bytef * buff_little  = alloca(BUFF_LITTLE);
  Bytef * buff_big = alloca( BUFF_BIG );
  int fd_in, fd_out, rv, rv_out;
  unsigned long not_compressed_data_size;
  unsigned long compressed_data_size;
  struct file_header * fh;
  
  

  /*    construct output file name */
  file_name_out = construct_name(file_name_in, F_DECOMPRESS);

  if (file_name_out == NULL) {
	return - ECANCELED;
  }

  /*    allocate memory for file header */
  fh = (struct file_header *) malloc(sizeof(struct file_header));
  
  if (fh == NULL ) {
	perror("Low mem: ");
	return - ENOMEM;
  }

  /*    open input file */
  fd_in = open (file_name_in, O_RDONLY);
  if (fd_in < 0) {
	perror("Cant open source: ");
	return - EIO;
  }

  /*    open output file */
  fd_out = open (file_name_out, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRWXG |  S_IRGRP);
  if (fd_out < 0) {
	perror("Cant open dest: ");
	return - EIO;
  }

  /*	read file header */
  rv = read (fd_in, fh, sizeof(struct file_header) );

  if (rv != sizeof(struct file_header) ) {
	perror("Couldn't read header:" );
	return - EIO;
  }
  
  /*	check singature */
  if ( CHECK_SINGATURE(fh->singature) != 0) {
	printf("Wrong singature!\n");
	return - ECANCELED;
  }

  /*	decompress file */
  do {

	/*	1. read size of biffer to read */
	rv = read (fd_in, &compressed_data_size, sizeof(compressed_data_size));
	if (rv != sizeof(compressed_data_size) ) {
//	  perror("Can't read size of buf; probably end of file:");
	  continue;
	}

	/*	2. read size of original uncompressed buffer */
	rv = read (fd_in, &not_compressed_data_size, sizeof(not_compressed_data_size));
	if (rv != sizeof(not_compressed_data_size) ) {
	  perror("Something wrong! Can't read size of not compressed buffer:");
	  return - ECANCELED;
	}

	/*	3. and now read the buffer */
	rv = read (fd_in, buff_little, compressed_data_size);
	if (rv != compressed_data_size) {
	  perror("Something wrong! Can't read compressed buffer:");
	  return - ECANCELED;
	}

	/*	decompress data. */
	rv_out = uncompress (buff_big, &not_compressed_data_size, buff_little, compressed_data_size);
	

	/*    check was there error or not */
	switch (rv_out) {
	  case Z_MEM_ERROR:
		printf("Compression memory error\n");
		return - ENOMEM;
		
	  case Z_BUF_ERROR:
		printf("Output buffer too small\n");
		return - ENOMEM;

	  case  Z_DATA_ERROR:
		printf("Input data is corrupted\n");
		return - ECANCELED;
		
	  default:
//		printf("out: %u\n",(unsigned int)compressed_data_size);
		break;
	}

	/*	write decompressed data to ouput file */
	rv_out = write (fd_out, buff_big, not_compressed_data_size);

	if (rv_out != not_compressed_data_size) {
	  perror ("Can't write into output file uncompressed data!");
	}

	

  } while (rv > 0);
  /*	close files */

  close (fd_in);
  close (fd_out);

  /*	rename (or move) decompressed file */
  create_path(extract_dir_name(fh->path));
  printf("rename:%s --> %s\n",file_name_out,fh->path);
  rv = rename (file_name_out, fh->path);

  /*    status of 'rename file' */
  if (rv < 0) {
	perror("Can't rename file:");
	return errno;
  }

  /*    change permissions of file */
  rv = chmod(fh->path, fh->stat.st_mode);
  /*    free file header */
  tfree (fh);
		
  /*	return */
  return 0;
  
}



int destroy_fl(struct flist * fl) {
  struct flist * fl_next = fl;
  if ( fl == NULL) {
	return - EINVAL;
  }
  while (fl != NULL) {
	tfree(fl->file_name);
	fl_next = fl->next;
	tfree(fl);
	fl = fl_next;
  }
  return 0;
}

struct flist * add_file(struct flist * fl, char * file_name) {
  struct flist * fl_ret, * fl_copy;
  
  if (file_name == NULL) {
	return NULL;
  }

  fl_copy = fl;

  /* create new node */

  fl_ret = (struct flist *) malloc(sizeof(struct flist));
  if (fl_ret == NULL) {
	return NULL;
  }

  fl_ret->file_name = strdup(file_name);
  fl_ret->next = NULL;

  if (fl == NULL) {
	return fl_ret;
  }
  else {
	while (fl_copy->next != NULL) {
	  fl_copy = fl_copy->next;
	}

	fl_copy->next = fl_ret;
	return fl_copy;
  }
  return NULL;
}


/*
	FUNCTION:	read_spec
	IN:			char *
	OUT:		stryct flist *
	Get name of spec file.
	Read spec file, split lines, cut '\n'.
	Add file names into linked list `flist`.
	Return linked list of file names.
	All lines begin with '#' are comments.
 
 */

struct flist * read_spec(char * file_name) {
  FILE * fd;
  char * buf = alloca (BUFF_BIG);
  char * buf_p = buf;
  char * rv;
  struct flist * fl = NULL;

  if (file_name == NULL) {
	return NULL;
  }

  /*	open config file */
  fd = fopen(file_name, "r");
  if (fd == NULL) {
	perror("Can't open spec");
	return NULL;
  }
  do {
	buf_p = buf;

	/*	clean buffer */
	memset (buf_p, 0, BUFF_BIG);

	/*	read string by string */
	rv = fgets(buf_p, BUFF_BIG, fd);

	/*	did read anything? */
	if (rv != buf_p) {
	  continue;
	}

	/*	split all tabs, spaces, etc */
	while (*buf_p == ' ' || *buf_p == '\t') {
	  buf_p++;
	}
	
	/* 	check if line commented or not */
	if (*buf_p == '#' ) {
	  continue;
	}

	/*	check end of line - I need cut it off */
	if (strlen (buf_p) > 0 &&  buf_p[strlen(buf_p) - 1] == '\n') {
	  buf_p[strlen(buf_p) - 1] = '\0';
	}

	/*	is there remain something at all? :-) */
	if (strlen (buf_p) == 0) {
	  continue;
	}

	/*	if `fl` isn't uninitialized yet */
	if (fl == NULL) {
	  fl = add_file(NULL, buf_p);
	  if (fl == NULL) {
		perror("error - can't add first file name to list");
		return NULL;
	  }
	}
	/*	else if it is */
	else {
	  if (add_file(fl, buf_p) == NULL) {
		perror("error - can't add file name to list");
		return NULL;
	  }
	}
	
  } while (rv != NULL);
  
return fl;	
}

#if DEBUG
/*
	FUNCTION:	print_fl
	IN:			struct flist *
	OUT:		nothing

	Get linked list of file names.
	Print file names.
	For debug only.
 */


void print_fl(struct flist *fl) {
  struct flist * fl_copy = fl;
  if (fl_copy == NULL) {
	printf("wrong argument\n");
	return;
  }
  while (fl_copy != NULL) {
	printf("file: %s\n",fl_copy->file_name);
	fl_copy = fl_copy->next;
  }

}
#endif /* DEBUG */

int find_file (struct flist *fl, char * file_name) {

  struct flist * fl_copy = fl;
  int strlen1, strlen2;
  
  if (fl_copy == NULL || file_name == NULL) {
	printf("RETURN error\n");
	return - EINVAL;
  }
  
  while (fl_copy != NULL) {

	/* strlen works faster that strcmp */
	strlen1 = strlen(fl_copy->file_name);
	strlen2 = strlen(file_name);

	printf("\n|%s||%s|\n",fl_copy->file_name,file_name);
	if (strlen1 != strlen2) {
	  printf("\nwrong : |%s||%s|\n",fl_copy->file_name,file_name);
	  goto n;
	}
	
	if (strcmp(fl_copy->file_name, file_name) == 0) {
	  printf("Found!\n");
	  return 0;
	}
n:
	fl_copy = fl_copy->next;
  }

  return - 1;

}

/*
	FUNCTION: 	validate_files
	IN:  		struct flist *
	OUT: 		int

	Get linked list of file names. Try to `stat()` every file.
	If any of these can't be `stat()` return error immediatly.
 
 */
int validate_files(struct flist *fl) {
  struct stat st;
  int rv;
  if (fl == NULL) {
	printf("wrong argument\n");
	return - EINVAL;
  }


  /*	go node by node */
  while (fl!=NULL) {
	DD("validate file: %s\n",fl->file_name);

	/*	try `stat() file` */
	rv = w_stat(fl->file_name, &st);
	if (rv != 0) {
	  switch (errno) {
		case EBADF :
		  printf("filedes is bad.\n");
		  break;
		case ENOENT :
		  printf("A component of the path %s does not exist, or the path is an empty string.\n", fl->file_name);
		  break;
		case ENOTDIR :
		  printf("A component of the path is not a directory.\n");
		  break;
		case ELOOP :
		  printf("Too many symbolic links encountered while traversing the path.\n");
		  break;
		case EFAULT :
		  printf("Bad address\n");
		  break;
		case EACCES :
		  printf("Permission denied\n");
		  break;
		case ENOMEM :
		  printf("Out of memory (i.e. kernel memory)\n");
		  break;
		case ENAMETOOLONG :
		  printf("File name too long\n");
		  break;
	  }
	  return - errno;
	}
	
	fl = fl->next;
  }
  return 0;
}



int concat_files (char * file_name_out, struct flist * fl) {
  struct flist * fl_copy;
  struct file_header fh;
  char * buf = alloca (BUFF_BIG);
  int fd_in, fd_out, rv;
  struct stat st;
  char * file_name_in;
  

  if (file_name_out == NULL || fl == NULL) {
	return - EINVAL;
  }
  
  /*	open output file */
  fd_out = open(file_name_out, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRWXG | S_IRGRP);
  if (fd_out < 0 ) {
	perror("Can't open concat file");
	return - EIO;
  }

  /* fill header of all update */
  fh.minor = fh.major = 0;
  strcpy (fh.description, "Update");
  memcpy (&fh.singature, SINGATURE, SIZE_OF_SINGATURE);

  if ( find_file(fl,"update.run") == 0) {
	printf("Set flag UPD_INSTALL_EXEC\n");
	SET_FLAG(fh.flags, UPD_HAS_PREINSTALL)
  }

  rv = write (fd_out, &fh, sizeof(fh));
  if (rv != sizeof(fh)) {
	perror ("Can't write header");
	return - EIO;
  }
  
  fl_copy = fl;
  while (fl_copy != NULL) {
	/*	construct name of compressed file from the name of regular file */
	file_name_in = construct_name(fl_copy->file_name, F_COMPRESS);

	/*	try stat() input file - anyway, I need size of the file */
	rv = w_stat(file_name_in, &st);

	if (rv < 0) {
	  perror("Can't stat file");
	  return - EIO;
	}


	/*	open input file */
	fd_in = open(file_name_in,O_RDONLY);
	if (fd_in < 0) {
	  perror("Can't open compressed input file");
	  return - EIO;
	}

	/*	write len of file */
	rv = write(fd_out, &st.st_size, sizeof(st.st_size));
	if (rv != sizeof(st.st_size) ) {
	  perror("Can't write to concat file");
	  return - EIO;
	}

	/* write input file to output */
	do {

	  /*	read from input file */
	  rv = read (fd_in, buf, BUFF_BIG);

	  if (rv > 0) {
		int rv2;
		/*	write to concat file */
		rv2 = write(fd_out,buf,rv);

		/*	check if wrote ok. */
		if (rv2 != rv) {
		  perror("Couldn'r write buffer into concat file");
		  return - EIO;
		}
	  }
	} while (rv > 0);

	close (fd_in);
	unlink(file_name_in);
	fl_copy = fl_copy->next;

  }
 
 return 0; 
}

/**/

int list_files(char * file_name) {
  int fd;
  size_t offset;
  struct stat st;
  int rv;
  struct file_header fh, fh_update;
  if (file_name == NULL) {
	return - EINVAL;
  }

  rv = w_wstat(file_name, &st);
  if (rv < 0 ) {
	perror("");
	return - errno;
  }

  fd = open (file_name, O_RDONLY);

  if (fd < 0) {
	perror("Can't open file");
	return - errno;
  }

  rv = read(fd, &fh_update, sizeof(fh_update));
  
  if (rv != sizeof(fh_update)) {
	perror("Can't read update header");
  }

//  if (memcmp (&fh_update.singature, SINGATURE, SIZE_OF_SINGATURE) != 0) {
  
  if ( CHECK_SINGATURE(&fh_update.singature) != 0) {
	printf("Wrong singature of update\n");
	return - ECANCELED;
  }

  printf ("Update packet: %s\n", fh_update.path);

  if (CHECK_FLAG(fh_update.flags, UPD_HAS_PREINSTALL)) {
	printf ("Update has install script\n");
  }

  printf ("Update major-minor: %i-%i\n", (int) fh_update.major, (int) fh_update.minor);
  printf ("Update description: %s\n", fh_update.description);
  printf("---------------------------------------------\n");

  do {
  	rv = read(fd, &offset, sizeof(offset));

	if (rv != sizeof(offset)) {
  	  continue;
  	}

  	rv = read(fd, &fh, sizeof(fh));
  	if (rv != sizeof(fh)) {
  	  continue;
  	}

//	if (memcmp(&fh.singature, SINGATURE, SIZE_OF_SINGATURE) != 0) {
  
	if ( CHECK_SINGATURE(&fh.singature) != 0) {
	  printf ("wrong singature!\n");
	  return - ECANCELED;
	}

	printf("Name:		%s\n", fh.path);
	printf("Size:		%i\n", (int) fh.stat.st_size);
	printf("Major:		%i\n", fh.major);
	printf("Minor: 		%i\n", fh.minor);
	printf("Description: %s\n",fh.description);
	printf("---------------------------------------------\n");

	rv = lseek(fd, offset - sizeof(fh),SEEK_CUR);

  
  } while (offset < st.st_size && rv > 0); 


  return 0;  

}

/*
 */

int write_description_and_name (char * file_name, char * description, char * name) {
  int fd;
  int rv;

  struct file_header fh;
  if (file_name == NULL) {
	return - EINVAL;
  }

  /*	open file */
  fd = open(file_name, O_RDWR);
  if (fd < 0) {
	perror("Can't open file");
	return - EIO;
  }

  /*	read header of update */
  rv = read (fd, &fh, sizeof(fh));
  if (rv != sizeof(fh)) {
	return - EIO;
  }

  /*	validate singature */
	if ( CHECK_SINGATURE(fh.singature) != 0) {
	printf("It not seems as update file\n");
	return - ECANCELED;
  }

  /*	if description given - copy to header. */
  if (description != NULL) {
	strncpy (fh.description, description, SIZE_OF_DESCRIPTION);
  }

  /*	if name given - copy to header */
  if (name != NULL) {
	strncpy (fh.path, name, SIZE_OF_PATH);
  }

  rv = lseek (fd, 0, SEEK_SET);
  if (rv < 0) {
	return - EIO;
  }

  rv = write(fd, &fh, sizeof(fh));
  if (rv != sizeof(fh)) {
	return - EIO;
  }
  close (fd);

  return 0;
}


/*
	FUNCTION: 	extract_dir_name
	IN:			char *
	OUT:		char *
	Get file name with path. Extract and return  path.
*/
char * extract_dir_name ( const char *file_name ) {
  char * buf;
  int  end_of_string;
  if (file_name == NULL || strlen(file_name) == 0) {
//	perror("1031 file_name is NULL");
	return NULL;
  }
//  DD("Got: %s\n", file_name);
  buf = strdup(file_name);
  if (buf == NULL) {
	perror("Low mem\n");
	return NULL;
  }

//  strncpy(buf, file_name, SIZE_OF_PATH);
  end_of_string = strlen(buf) - 1;

  for(; end_of_string > 0; end_of_string-- ) {

	if (buf[end_of_string] == '/') {
  	  buf[end_of_string] = '\0';
	  return buf;
	}

	buf[end_of_string] = '\0';

  }

//  DD("1034 return NULL\n");
  return NULL;

}

/*
FUNCTION: 	create_path
IN:			char *
OUT:		int
Get directory name. Return 0 if success and not 0 on error.
If directory exists - success.
If not exists - try create it. 
If created OK return success.
 
 */

int create_path (char * dir) {
  DIR *dirp;
  int rv;

  if (dir == NULL) {
	return - EINVAL;
  }

  if (strlen(dir) == 0) {
	return 0;
  }

  DD("1055 Got dir name: %s\n",dir);

  /*	try to open directory */
  dirp = opendir(dir);

  /*	if can't open try to create dir */
  if (dirp != NULL) {
  	closedir (dirp);
	return 0;
  }
	

  rv = create_path (extract_dir_name( dir ) );
  if (rv == 0)
  {
  	/*	if can't create dir it seems to be multilayer path*/
  	DD("try create:%s\n",dir);
  	return mkdir(dir , S_IRWXU);
  }

  return 0;
}


/*
	FUNCTION: 	find_and_extract_file
	IN:			char * file_name_in, char * file_name_out
	OUT:		int
*/

int find_and_extract_file(char * file_name_in, char * file_name_out) {
  if (file_name_in == NULL || file_name_out == NULL) {
	return - EINVAL;
  }
  
  return 0;
}

